//==============================================================================
//
//   DSHA256 - the NIST's Secure Hash Algorithm 256 class in the ofc-library
//
//               Copyright (C) 2007  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2007-07-12 17:24:27 $ $Revision: 1.3 $

//==============================================================================

#include "ofc/DSHA256.h"

#include <string.h>


#if _INTERFACE_

#include <stdint.h>

#include "ofc/config.h"

#include "ofc/DText.h"
#include "ofc/DData.h"

//
// The DSHA256 class implements a number of methods for using the NIST's SHA256
// algorithm. 
//

@interface DSHA256 : Object
{
@private
  uint32_t      _h[8];
  uint32_t      _count[2];
  unsigned char _input[64];
}

#endif



@implementation DSHA256

  
#if _PRIVATE_

static uint32_t _k[64] =
{
   0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
   0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
   0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
   0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
   0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
   0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
   0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
   0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

#define rrol(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))

//
// Update the SHA256 state with input
//
// @param hh    the state of sha256
// @param input the extra input
// 
// @return none
//

static void _update(uint32_t hh[8], uint32_t input[16])
{
  uint32_t a = hh[0];
  uint32_t b = hh[1];
  uint32_t c = hh[2];
  uint32_t d = hh[3];
  uint32_t e = hh[4];
  uint32_t f = hh[5];
  uint32_t g = hh[6];
  uint32_t h = hh[7];
  
  uint32_t w[64];
  
  int i;

#ifdef WORDS_BIGENDIAN
  memcpy(w, input, sizeof(input));
#else
  for (i = 0; i < 16; i++)
  {
    w[i] = ((input[i] >> 24) & 0x000000ff) |
           ((input[i] >> 8)  & 0x0000ff00) | 
           ((input[i] << 8)  & 0x00ff0000) |
           ((input[i] << 24) & 0xff000000);
  }
#endif
  
  for (i = 16; i < 64; i++)
  {
    uint32_t s0 = (rrol(w[i-15], 7)) ^ (rrol(w[i-15], 18)) ^ (w[i-15] >> 3);
    uint32_t s1 = (rrol(w[i-2], 17)) ^ (rrol(w[i-2],  19)) ^ (w[i-2]  >> 10);
    
    w[i] = w[i-16] + s0 + w[i-7] + s1;
  }

  
  for (i = 0; i < 64; i++)
  {
    uint32_t t1  = h + ((rrol(e, 6)) ^ (rrol(e, 11)) ^ (rrol(e, 25))) + ((e & f) ^ ((~e) & g)) + _k[i] + w[i];
    uint32_t t2  = ((rrol(a, 2)) ^ (rrol(a, 13)) ^ (rrol(a, 22))) + ((a & b) ^ (a & c) ^ (b & c));
    
    h = g;
    g = f;
    f = e;
    e = d + t1;
    d = c;
    c = b;
    b = a;
    a = t1 + t2;
  }

  hh[0] += a;
  hh[1] += b;
  hh[2] += c;
  hh[3] += d;
  hh[4] += e;
  hh[5] += f;
  hh[6] += g;
  hh[7] += h;
}

#endif


//// Constructors

//
// Initialise the sha256 object
//
// @return the object
//
- (DSHA256 *) init
{
  [super init];

  [self reset];
  
  return self;
}

//
// Initialise the sha256 object with a string
//
// @param cstring  the c-string
//
// @return the object
//
- (DSHA256 *) init :(const char *) cstring
{
  [self init];

  [self update :cstring];

  return self;
}

//
// Initialise the sha256 object with data
//
// @param data     the data ('\0' allowed)
// @param length   the length of the data
//
// @return the object
//
- (DSHA256 *) init :(const unsigned char *) data :(unsigned long) length
{
  [self init];

  [self update :data :length];

  return self;
}


//// Reset method

//
// Reset the sha256 object 
//
// @return the object
//
- (DSHA256 *) reset
{
  _h[0] = 0x6a09e667;
  _h[1] = 0xbb67ae85;
  _h[2] = 0x3c6ef372;
  _h[3] = 0xa54ff53a;
  _h[4] = 0x510e527f;
  _h[5] = 0x9b05688c;
  _h[6] = 0x1f83d9ab;
  _h[7] = 0x5be0cd19;

  _count[0] = 0;
  _count[1] = 0;

  return self;
}


//// Update methods

//
// Update the sha256 object with data. Repeated calls can be compared
// to a single call in which the strings are concatenated
//
// @param data     the data to be feeded to sha256
// @param length   the length of the data
//
// @return the object
//
- (DSHA256 *) update :(const unsigned char *) data :(unsigned long) length
{
  if (length > 0)
  {
    unsigned int i, j;

    j = (_count[0] >> 3) & 63;
    
    if ((_count[0] += length << 3) < (length << 3)) 
      _count[1]++;
    
    _count[1] += (length >> 29);

    if ((j + length) > 63) 
    {
      memcpy(&_input[j], data, (i = 64-j));

      _update(_h, (uint32_t *) _input);
      
      for ( ; i + 63 < length; i += 64) 
      {
        _update(_h, (uint32_t *) &data[i]);
      }
      j = 0;
    }
    else 
      i = 0;
    
    memcpy(&_input[j], &data[i], length - i);
  }

  return self;
}

//
// Update the object with a c-string. Repeated calls can be compared
// to a single call in which the strings are concatenated
//
// @param cstring  the c-string to be feeded to sha256
//
// @return the object
//
- (DSHA256 *) update :(const char *) cstring
{
  int length = (cstring != NULL) ? strlen(cstring) : 0;
  
  if (length > 0)
  {
    [self update :(unsigned char *) cstring :length];
  }

  return self;
}


//// Digest methods

//
// Return the (current) digest of all data passed to the object. 
//
// @return the (new) data string object with the digest
//
- (DData *) digest
{
  unsigned long  i;
  unsigned char  finalcount[8];
  unsigned char  digest[32];
  DData         *data = [DData alloc];
  
  uint32_t       h[8];
  uint32_t       count[2];
  unsigned char  buffer[64];

  // save the current state
  memcpy(h,      _h,  sizeof(_h));
  memcpy(count,  _count,  sizeof(_count));
  memcpy(buffer, _input, sizeof(_input));
  
  for (i = 0; i < 8; i++) 
  {
    finalcount[i] = (unsigned char)((_count[(i >= 4 ? 0 : 1)]
                    >> ((3-(i & 3)) * 8) ) & 255);  // Endian independent
  }

  [self update :(unsigned char *) "\200" :1];  // bit 1
  
  while ((_count[0] & 504) != 448) 
  {
    [self update :(unsigned char *) "\0"  :1];
  }
  [self update :finalcount :8];
  
  for (i = 0; i < sizeof(digest) / sizeof(digest[0]) ; i++) 
  {
    digest[i] = (unsigned char) ((_h[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
  }

  // restore the state
  memcpy(_input, buffer, sizeof(_input));
  memcpy(_h,      h,      sizeof(_h));
  memcpy(_count,  count,  sizeof(_count));
  
  return [data init :digest :32];
}

//
// Returns the (current) digest of all data passed to the object. The
// returning string contains the digest in hexadecimal ascii characters.
//
// @return the (new) string object with the digest
//
- (DText *) hexdigest
{
  DData *data   = [self digest     ];
  DText *string = [data tohexString];
  
  [data free];
  
  return string;
}

@end

/*===========================================================================*/

